/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE").
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR
 * COPYRIGHT LAW IS PROHIBITED.
 *
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE
 * MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 *
 */
package l1j.server.server.model.game;

import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.opqlo.Server.L1World.L1World;
import l1j.server.Config;
import l1j.server.Thread.GeneralThreadPool;
import l1j.server.server.IdFactory;
import l1j.server.server.datatables.DoorSpawnTable;
import l1j.server.server.datatables.NpcTable;
import l1j.server.server.datatables.RaceTicketTable;
import l1j.server.server.datatables.ShopTable;
import l1j.server.server.model.L1Location;
import l1j.server.server.model.L1Object;
import l1j.server.server.model.Instance.L1DoorInstance;
import l1j.server.server.model.Instance.L1MerchantInstance;
import l1j.server.server.model.Instance.L1NpcInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.Instance.NpcMoveExecutor;
import l1j.server.server.model.shop.L1Shop;
import l1j.server.server.serverpackets.S_NPCPack;
import l1j.server.server.serverpackets.S_NpcChatPacket;
import l1j.server.server.templates.L1RaceTicket;
import l1j.server.server.templates.L1ShopItem;
import l1j.server.server.utils.Random;

public class L1BugBearRace {
	private static final Logger _log = Logger.getLogger(L1BugBearRace.class.getName());
	private class BugBearRunning extends TimerTask {
		L1NpcInstance _bugBear;
		int _runnerNumber;

		BugBearRunning(final int runnerNumber) {
			this._bugBear = L1BugBearRace.this._runner[runnerNumber];
			this._runnerNumber = runnerNumber;
		}

		public void begin(final int startTime) {
			GeneralThreadPool.getInstance().schedule(this, startTime);
		}

		@Override
		public void run() {
			int sleepTime = 0;
			while (L1BugBearRace.this.getGameStatus() == STATUS_PLAYING) {
				try {
					Thread.sleep(sleepTime);
				} catch (final InterruptedException e) {
					_log.log(Level.SEVERE, e.getLocalizedMessage() , e);
				}
				try{
					while (!this._bugBear.getMap().isPassable(this._bugBear.getX(),this._bugBear.getY(), this._bugBear.getHeading())) {
						if(!Config.BugbearRace_Switch){
							break;
						}
						if (this._bugBear.getMap().isPassable(this._bugBear.getX(),this._bugBear.getY(), this._bugBear.getHeading() + 1)) {
							this._bugBear.setHeading(L1BugBearRace.this.rePressHeading(this._bugBear.getHeading() + 1));
							break;
						} else {
							this._bugBear.setHeading(L1BugBearRace.this.rePressHeading(this._bugBear.getHeading() - 1));
							if (this._bugBear.getMap().isPassable(this._bugBear.getX(),this._bugBear.getY(), this._bugBear.getHeading())) {
								break;
							}
						}
					}
					final NpcMoveExecutor move = this._bugBear.getMove();
					if(move != null){
						move.setDirectionMove(this._bugBear.getHeading());// ヘッジ方向
					}
					if (L1BugBearRace.this.checkPosition(this._runnerNumber)) {
						this._bugBear = null;
						this.cancel();
						return;
					}else if(L1BugBearRace.this.getGameStatus()==3){
						this.cancel();
						return;
					} else {
						// new BugBearRunning(_runnerNumber).
						// インスタンスを生成しないでください　メモリリークが発生します
						sleepTime = L1BugBearRace.this.calcSleepTime(this._bugBear.getPassispeed(),this._runnerNumber);
					}
				}catch(Exception e){
					this.cancel();
					_log.log(Level.SEVERE, e.getLocalizedMessage() , e);
				}
			}
		}

	}
	private class RaceTimer extends TimerTask {
		int _startTime;

		public void begin() {
			GeneralThreadPool.getInstance().schedule(this, this._startTime * 1000);
		}
		@Override
		public void run() {
			if(Config.BugbearRace_Switch){
				try {
					// ゲームのステータスをNONEに（10分前）
					L1BugBearRace.this.setGameStatus(STATUS_NONE);
					L1BugBearRace.this.sendMessage("$376 10 $377");
					//				L1BugBearRace.this.BroadCastToAll("<賽肥資訊>【五】分後開局，買票者盡速從快");

					for (int loop = 0; loop < WAIT_TIME; loop++) {
						try {
							Thread.sleep(1000);
						} catch (final InterruptedException e) {
							_log.log(Level.SEVERE, e.getLocalizedMessage() , e);
						}
					}

					L1BugBearRace.this.clearRunner();
					L1BugBearRace.this.setRound(L1BugBearRace.this.getRound() + 1);
					/* レース回数-記録処理 */
					final L1RaceTicket ticket = new L1RaceTicket();
					ticket.set_itemobjid(FIRST_ID);// 重複可能
					ticket.set_allotment_percentage(0);
					ticket.set_round(L1BugBearRace.this.getRound());
					ticket.set_runner_num(0);
					ticket.set_victory(0);
					RaceTicketTable.getInstance().storeNewTiket(ticket);// 記録用
					RaceTicketTable.getInstance().oldTicketDelete(L1BugBearRace.this.getRound());// 古い記録を削除
					/**/
					L1BugBearRace.this.setRandomRunner();// ランナー準備
					L1BugBearRace.this.setRandomCondition();
					/* SHOP BuyList格納処理 */

					for (int i = 0; i < 5; i++) {
						//L1ShopItem shopItem1 = new L1ShopItem(40309, 10, 1,0,null);
						final L1ShopItem shopItem1 = new L1ShopItem(40309, 10, 1,0,null,0);
						shopItem1.setName(i);
						shop1.getSellingItems().add(shopItem1);
						final L1ShopItem shopItem2 = new L1ShopItem(40309, 10, 1,0,null,0);
						shopItem2.setName(i);
						shop2.getSellingItems().add(shopItem2);
						final L1ShopItem shopItem3 = new L1ShopItem(40309, 10, 1,0,null,0);
						shopItem3.setName(i);
						shop3.getSellingItems().add(shopItem3);
					}
					/**/
					L1BugBearRace.this.setWinnigAverage();
					L1BugBearRace.this.setGameStatus(STATUS_READY);
					for (int loop = 0; loop < READY_TIME - 1; loop++) {
						if (loop % 60 == 0) {
							L1BugBearRace.this.sendMessage("$376 " + (1 + (READY_TIME - loop) / 60)+ " $377");
							//	}else if(loop == 241){
							//	BroadCastToAll("<賽肥資訊>【五】分後開局，買票者盡速從快");
							//					}else if(loop == 481){
							//						L1BugBearRace.this.BroadCastToAll("<賽肥資訊>【一】分後開局，買票者盡速從快");
						}
						Thread.sleep(1000);
					}
					L1BugBearRace.this.sendMessage("$363");// 363 レディー！
					Thread.sleep(1000);
					for (int loop = 10; loop > 0; loop--) {
						L1BugBearRace.this.sendMessage("" + loop);
						Thread.sleep(1000);
					}
					L1BugBearRace.this.sendMessage("$364");// 364 ゴー！
					L1BugBearRace.this.setGameStatus(STATUS_PLAYING);
					/* SHOP BuyListから削除 */

					shop1.getSellingItems().clear();
					shop2.getSellingItems().clear();
					shop3.getSellingItems().clear();
					/**/
					L1BugBearRace.this.DoorCtrl(true);
					/*	final L1DoorInstance[] doorlist = DoorSpawnTable.getInstance().getDoorList();
				for (final L1DoorInstance door :  doorlist) {

						switch(door.getDoorId()){
						case 808:
						case 809:
						case 810:
						case 811:
						case 812:
							door.open();
							break;
					}
				}*/
					for (int i = 0; i < L1BugBearRace.this._runner.length; i++) {
						new BugBearRunning(i).begin(0);
					}

					new StartBuffTimer().begin();

					for (int i = 0; i < L1BugBearRace.this._runner.length; i++) {
						if (L1BugBearRace.this.getBetCount(i) > 0) {
							final double oo=(Random.nextInt(18)+1)*0.1;
							double alloo = L1BugBearRace.this.getAllBet() / L1BugBearRace.this.getBetCount(i) / 10D + oo;
							if(alloo<=1){
								L1BugBearRace.this._allotment_percentage[i] = 1+oo;
							}else if(alloo>=10){
								alloo=10;
							}else{
								L1BugBearRace.this._allotment_percentage[i] = alloo;
							}
						} else {
							//_allotment_percentage[i] = 0.0;
							L1BugBearRace.this._allotment_percentage[i] = Random.nextInt(5)+Random.nextInt(9)*0.1+1;
						}
					}
					for (int i = 0; i < L1BugBearRace.this._runner.length; i++) {
						Thread.sleep(1000);
						String per=String.valueOf(L1BugBearRace.this._allotment_percentage[i]);
						if(per.length()>4) {
							per=per.substring(0, 4);
						}
						final String s =NpcTable.getInstance()
								.getTemplate(L1BugBearRace.this._runner[i].getNpcId()).get_nameid()
								+ " $402 "// 一文字3バイトだが面倒なのでネームIDを復元しない・・・
								//+ String.valueOf(_allotment_percentage[i]));// 402// の配当率は
								+ per;

						L1BugBearRace.this.sendMessage(s);// 402

					}
					this.cancel();
				}catch(Exception e){
					this.cancel();
					_log.log(Level.SEVERE, e.getLocalizedMessage() , e);
				}
			}
		}

		public void setTimer(final int startTime) {
			this._startTime = startTime;
		}
	}
	private class StartBuffTimer extends TimerTask {
		StartBuffTimer() {
		}

		public void begin() {
			GeneralThreadPool.getInstance().scheduleAtFixedRate(this, 1000, 1000);
		}

		@Override
		public void run() {
			if (L1BugBearRace.this.getGameStatus() == STATUS_PLAYING) {
				for (int i = 0; i < L1BugBearRace.this._runner.length; i++) {
					if ( L1BugBearRace.this.getRandomProbability() <= L1BugBearRace.this._winning_average[i]* (1 + 0.2 * L1BugBearRace.this.getCondition(i))) {
						L1BugBearRace.this._runner[i].setBraveSpeed(1);
					} else {
						L1BugBearRace.this._runner[i].setBraveSpeed(0);
					}
				}
			} else {
				this.cancel();
			}
		}
	}
	//private static L1MerchantInstance pory;
	//private static L1MerchantInstance cecile;
	L1MerchantInstance parkin;
	private static final int FIRST_ID = 0x0000000;
	private static final int STATUS_NONE = 0;
	private static final int STATUS_READY = 1;
	private static final int STATUS_PLAYING = 2;
	private static final int STATUS_END = 3;
	private static final int WAIT_TIME = 60;//60;
	private static final int READY_TIME = 9 * 60 - 10;// test 60;//
	private static final int FIRST_NPCID = 91350;// ~20
	public static L1BugBearRace getInstance() {
		if (instance == null) {
			instance = new L1BugBearRace();
		}
		return instance;
	}
	private final L1NpcInstance[] _runner;
	private final int[] _runnerStatus = new int[5];
	private final double[] _winning_average = new double[5];
	private final double[] _allotment_percentage = new double[5];
	private final int[] _condition = new int[5];;
	private long _allBet;
	private final int[] _betCount = new int[5];
	private static RaceTimer _RaceTimer;
	private static L1DoorInstance[] _door = new L1DoorInstance[5];
	private int _round;

	private static final L1Shop shop1 = ShopTable.getInstance().get(70035);

	private static final L1Shop shop2 = ShopTable.getInstance().get(70041);
	private static final L1Shop shop3 = ShopTable.getInstance().get(70042);

	private static L1BugBearRace instance;

	private int _status = 0;

	L1BugBearRace() {
		this.setRound(RaceTicketTable.getInstance().getRoundNumOfMax());
		this._runner = new L1NpcInstance[5];

		for (final L1Object obj : L1World.getInstance().getObject()) {
			if(this.parkin != null){
				break;
			}
			if (obj instanceof L1MerchantInstance) {
				switch(((L1MerchantInstance) obj).getNpcId()){
				case 70041:
					this.parkin = (L1MerchantInstance) obj;
					break;
					/*case 70035:
					cecile = (L1MerchantInstance) obj;
					break;
				case 70042:
					pory = (L1MerchantInstance) obj;
					break;*/
				}
			}
		}
		_RaceTimer = new RaceTimer();
		_RaceTimer.setTimer(0);
		_RaceTimer.begin();
	}

	private int calcSleepTime(int sleepTime,final int runnerNumber) {
		final L1NpcInstance npc = this._runner[runnerNumber];
		if (npc.getBraveSpeed() == 1) {
			sleepTime -= sleepTime * 0.25;
		}
		return sleepTime;
	}

	private boolean checkDuplicate(final int npcid,final int curi) {
		for (int i = 0; i < curi; i++) {
			if (this._runner[i] != null) {
				if (this._runner[i].getNpcId() == npcid) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean checkDuplicateAverage(final double winning_average,final  int curi) {
		// 勝率跟狀態都一樣算重覆
		for (int i = 0; i < curi; i++) {
			if (this._winning_average[i] == winning_average && this._condition[i] == this._condition[curi]) {
				return true ;
			}
		}
		return false;
	}

	public boolean checkPosition(final int runnerNumber) {// 現在のポジションを確認
		final int[] defaultHead = { 6, 7, 0, 1, 2, 2 };
		if (this.getGameStatus() != STATUS_PLAYING) {
			return false;
		}
		boolean flag = false;// ゴールするまではfalseを返す
		final L1NpcInstance npc = this._runner[runnerNumber];
		final int x = npc.getX();
		final int y = npc.getY();

		switch(this._runnerStatus[runnerNumber]){
		case 0:// スタート　直線
			if (x >= 33476 && x <= 33476 + 8 && y >= 32861 && y <= 32861 + 8) {
				this._runnerStatus[runnerNumber] = this._runnerStatus[runnerNumber] + 1;
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを変更
			} else {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを復元
			}
			break;
		case 1:
			if (x <= 33473 && x >= 33473 - 9 && y == 32858) {
				this._runnerStatus[runnerNumber] = this._runnerStatus[runnerNumber] + 1;
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを変更
			} else {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを復元
			}
			break;
		case 2:
			if (x <= 33473 && x >= 33473 - 9 && y == 32852) {
				this._runnerStatus[runnerNumber] = this._runnerStatus[runnerNumber] + 1;
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを変更
			} else {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを復元
			}
			break;

		case 3:
			if (x == 33478 && y <= 32847 && y >= 32847 - 9) {
				this._runnerStatus[runnerNumber] = this._runnerStatus[runnerNumber] + 1;
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを変更
			} else {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを復元
			}
			break;
		case 4:
			if (x == 33523 && y >= 32847 - 9 && y <= 32847) {
				this._runnerStatus[runnerNumber] = this._runnerStatus[runnerNumber] + 1;
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを変更
				// goal
				this.goal(runnerNumber);
			} else {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを復元
			}
			break;

		case 5:
			if (x == 33527 && y >= 32847 - 8 && y <= 32847) {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを変更
				this.finish();
				flag = true;
			} else {
				npc.setHeading(defaultHead[this._runnerStatus[runnerNumber]]);// ヘッジを復元
			}
			break;
		}

		return flag;
	}

	private void clearRunner() {
		for (int i = 0; i < 5; i++) {
			if (this._runner[i] != null) {
				this._runner[i].deleteMe();
				if (this._runner[i].getMap().isInMap(this._runner[i].getX(),this._runner[i].getY())) {
					this._runner[i].getMap().setPassable(this._runner[i].getX(),this._runner[i].getY(), true);
				}
			}
			this._runner[i] = null;
			this._runnerStatus[i] = 0;
			this._condition[i] = 0;
			this._winning_average[i] = 0;
			this._allotment_percentage[i] = 0.0;
			this.setBetCount(i, 0);
		}
		this.setAllBet(0);
		this.DoorCtrl(false);
		/*final L1DoorInstance[] doorlist = DoorSpawnTable.getInstance().getDoorList();
		for (final L1DoorInstance door :  doorlist) {

				switch(door.getDoorId()){
				case 808:
				case 809:
				case 810:
				case 811:
				case 812:
					door.close();
					break;
			}
		}*/
	}

	// TODO 周回数判定処理　end

	private void DoorCtrl(final boolean open){

		if(_door[0] ==null){

			final L1DoorInstance[] doorlist = DoorSpawnTable.getInstance().getDoorList();
			for (final L1DoorInstance door :  doorlist) {
				switch(door.getDoorId()){
				case 808:
					_door[0] = door;
					break;
				case 809:
					_door[1] = door;
					break;
				case 810:
					_door[2] = door;
					break;
				case 811:
					_door[3] = door;
					break;
				case 812:
					_door[4] = door;
					break;
				}
			}
		}if(open){
			for (final L1DoorInstance door : _door){
				door.open();
			}
		}else{
			for (final L1DoorInstance door : _door){
				door.close();
			}
		}

	}

	private void finish() {
		int cnt = 0;
		for (final int _runnerStatu : this._runnerStatus) {
			if (_runnerStatu == 5) {
				cnt++;
			}
		}
		if (cnt == 5) {
			this.setGameStatus(STATUS_END);
			_RaceTimer.cancel();
			_RaceTimer = new RaceTimer();
			_RaceTimer.setTimer(30);
			_RaceTimer.begin();
			/* SHOP格納処理 */

			/**/
		}
	}

	public long getAllBet() {
		return this._allBet;
	}

	public int getBetCount(final int i) {
		return this._betCount[i];
	}

	public int getCondition(final int num) {
		return this._condition[num];
	}

	public int getGameStatus() {
		return this._status;
	}

	private double getRandomProbability(){
		return (Random.nextInt(10000) + 1) / 100D;
	}

	public int getRound() {
		return this._round;
	}

	public L1NpcInstance getRunner(final int num) {
		return this._runner[num];
	}

	public double getWinningAverage(final int num) {
		return this._winning_average[num];
	}

	private void goal(int runnberNumber) {
		int cnt = 0;
		for (final int _runnerStatu : this._runnerStatus) {
			if (_runnerStatu == 5) {
				cnt++;
			}
		}
		if (cnt == 1) {
			this.parkin.wideBroadcastPacket(new S_NpcChatPacket(this.parkin, "第 "+ this.getRound()+ " $366 "
					+ NpcTable.getInstance().getTemplate(this._runner[runnberNumber].getNpcId()).get_nameid() + " $367", 2));// 5>3バイト
			/* DB格納処理 */
			RaceTicketTable.getInstance().updateTicket(this.getRound(),
					this._runner[runnberNumber].getNpcId() - FIRST_NPCID + 1,
					this._allotment_percentage[runnberNumber]);
			/**/
		}
	}

	private int rePressHeading(int heading) {
		if (heading < 0) {// 0未満ならば7
			heading = 7;

		}else if (heading > 7) {// 7より大きいなら0
			heading = 0;
		}
		return heading;
	}

	private void sendMessage(String id) {
		this.parkin.wideBroadcastPacket(new S_NpcChatPacket(this.parkin, id, 2));
		// cecile.broadcastPacket(new S_NpcChatPacket(cecile,id, 2));
		//pory.wideBroadcastPacket(new S_NpcChatPacket(pory, id, 2));
	}

	public void ServerReStart(){
		final int Status =this.getGameStatus();
		if(Status==1||Status==2){
			this.setGameStatus(3);
//			RaceTicketTable.getInstance().ServerReStart(this.getRound());
			this.clearRunner();
		}
	}

	public void setAllBet(final long allBet) {// allbetは3桁の整数
		this._allBet = (long) (allBet * 0.5);// 管理員抽成
	}
	public void setBetCount(final int i,final int count) {
		this._betCount[i] = count;
	}

	public void setGameStatus(int i) {
		this._status = i;
	}

	/*
	 * private void setCondition(int num, int condition) { this._condition[num]
	 * = condition; }
	 */

	private void setRandomCondition() {
		for (int i = 0; i < this._condition.length; i++) {
			this._condition[i] = -1 + Random.nextInt(3);
		}
	}

	private void setRandomRunner() {
		for (int i = 0; i < 5; i++) {
			int npcid = FIRST_NPCID + Random.nextInt(20);
			while (this.checkDuplicate(npcid, i)) {
				npcid = FIRST_NPCID + Random.nextInt(20);
			}
			final L1Location loc = new L1Location(33522 - i * 2, 32861 + i * 2, 4);
			this._runner[i] = this.spawnOne(loc, npcid, 6);

		}
	}

	private void setRound(int round) {
		this._round = round;
	}

	private void setWinnigAverage() {
		for (int i = 0; i < this._winning_average.length ; i++) {
			double winningAverage = this.getRandomProbability();

			while (this.checkDuplicateAverage(winningAverage, i)) {
				winningAverage = this.getRandomProbability();
			}
			this._winning_average[i] = winningAverage;
		}
	}
	/**
	 * 指定されたロケーションに任意のNpcを一匹生成する。
	 *
	 * @param loc
	 *            出現位置
	 * @param npcid
	 *            任意のNpcId
	 * @param heading
	 *            向き
	 * @return L1NpcInstance 戻り値 : 成功=生成したインスタンス 失敗=null
	 */
	private L1NpcInstance spawnOne(final L1Location loc,final int npcid,final int heading) {
		final L1NpcInstance mob = new L1NpcInstance(NpcTable.getInstance().getTemplate(npcid));
		if (mob != null) {
			mob.setNameId("#" + (mob.getNpcId() - FIRST_NPCID + 1) + " "+ mob.getNameId());
			mob.setId(IdFactory.getInstance().nextId());
			mob.setHeading(heading);
			mob.setX(loc.getX());
			mob.setHomeX(loc.getX());
			mob.setY(loc.getY());
			mob.setHomeY(loc.getY());
			mob.setMap((short) loc.getMapId());
			mob.setPassispeed(mob.getPassispeed() * 2);
			L1World.getInstance().storeObject(mob);
			L1World.getInstance().addVisibleObject(mob);

			final S_NPCPack s_npcPack = new S_NPCPack(mob);
			for (final L1PcInstance pc : L1World.getInstance().getRecognizePlayer(mob)) {
				pc.addKnownObject(mob);
				mob.addKnownObject(pc);
				pc.sendPackets(s_npcPack);
			}
			// モンスターのＡＩを開始
			mob.onNpcAI();
			mob.turnOnOffLight();
			mob.startChat(L1NpcInstance.CHAT_TIMING_APPEARANCE); // チャット開始
		}
		return mob;
	}
}
